import path from 'path';
import { fileURLToPath } from 'url';
import grpc from '@grpc/grpc-js';
import protoLoader from '@grpc/proto-loader';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

class GrpcClient {
    constructor() {
        this.clients = {};
        this.isConnected = false;
    }

    async connect(address = 'localhost:51051', protoPath) {
        console.log('Connecting to gRPC server at', address);

        let PROTO_PATH;
        if (protoPath) {
            PROTO_PATH = protoPath;
        } else {
            // 假设 proto 文件在项目根目录的 src/protos 文件夹中
            PROTO_PATH = path.join(__dirname, '..', 'src', 'protos', 'sushi_rpc.proto');
        }
        console.log('Loading proto file from:', PROTO_PATH);
        
        const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
            keepCase: true,
            longs: String,
            enums: String,
            defaults: true,
            oneofs: true
        });
        
        const sushiProto = grpc.loadPackageDefinition(packageDefinition).sushi_rpc;
        // console.log('Available services:', Object.keys(sushiProto));

        // 初始化所有服务的客户端
        const services = ['AudioGraphController', 'ParameterController', 'TransportController', 'SystemController'];
        for (const service of services) {
            if (sushiProto[service]) {
                this.clients[service] = new sushiProto[service](address, grpc.credentials.createInsecure());
                console.log(`Initialized ${service} client`);
            } else {
                console.warn(`Service ${service} not found in proto definition`);
            }
        }

        // 等待所有客户端准备就绪
        try {
            await Promise.all(Object.values(this.clients).map(client => 
                new Promise((resolve, reject) => {
                    client.waitForReady(Date.now() + 5000, (error) => {
                        if (error) {
                            console.error(`Failed to connect ${client.constructor.name}:`, error);
                            reject(error);
                        } else {
                            console.log(`${client.constructor.name} connected successfully`);
                            resolve();
                        }
                    });
                })
            ));
            this.isConnected = true;
            console.log('All gRPC clients connected successfully');
        } catch (error) {
            this.isConnected = false;
            console.error('Failed to connect all gRPC clients:', error);
            throw error;
        }
    }

    deleteProcessorFromTrack(processorId, trackId) {
        return new Promise((resolve, reject) => {
            const client = this.clients.AudioGraphController;
            if (!client) {
                reject(new Error('AudioGraphController client is not initialized'));
                return;
            }
            
            console.log('Calling DeleteProcessorFromTrack');
            const request = {
                processor: { id: processorId },
                track: { id: trackId }
            };
            
            client.DeleteProcessorFromTrack(request, (err, response) => {
                if (err) {
                    console.error('Error in DeleteProcessorFromTrack:', err);
                    reject(err);
                } else {
                    console.log('DeleteProcessorFromTrack response:', response);
                    resolve(response);
                }
            });
        });
    }

    createProcessorOnTrack(name, uid, path, pluginType, trackId, position) {
        return new Promise((resolve, reject) => {
            const client = this.clients.AudioGraphController;
            if (!client) {
                reject(new Error('AudioGraphController client is not initialized'));
                return;
            }
            
            const request = {
                name,
                uid,
                path,
                type: { type: pluginType },
                track: { id: trackId },
                position: {
                    add_to_back: position.addToBack,
                    before_processor: position.beforeProcessor ? { id: position.beforeProcessor } : null
                }
            };
            
            client.CreateProcessorOnTrack(request, (err, response) => {
                if (err) {
                    console.error('Error in CreateProcessorOnTrack:', err);
                    reject(err);
                } else {
                    console.log('CreateProcessorOnTrack response:', response);
                    resolve(response);
                }
            });
        });
    }

    setParameterValue(processorId, parameterId, value) {
        return new Promise((resolve, reject) => {
            const client = this.clients.ParameterController;
            if (!client) {
                reject(new Error('ParameterController client is not initialized'));
                return;
            }
            
            const request = {
                parameter: {
                    processor_id: processorId,
                    parameter_id: parameterId
                },
                value: value
            };
            
            client.SetParameterValue(request, (err, response) => {
                if (err) {
                    console.error('Error in SetParameterValue:', err);
                    reject(err);
                } else {
                    // console.log('SetParameterValue response:', response);
                    resolve(response);
                }
            });
        });
    }

    moveProcessorOnTrack(processorId, sourceTrackId, destTrackId, position) {
        return new Promise((resolve, reject) => {
            const client = this.clients.AudioGraphController;
            if (!client) {
                reject(new Error('AudioGraphController client is not initialized'));
                return;
            }
            
            console.log('Calling MoveProcessorOnTrack');
            const request = {
                processor: { id: processorId },
                source_track: { id: sourceTrackId },
                dest_track: { id: destTrackId },
                position: {
                    add_to_back: position.addToBack,
                    before_processor: position.beforeProcessor ? { id: position.beforeProcessor } : null
                }
            };
            
            client.MoveProcessorOnTrack(request, (err, response) => {
                if (err) {
                    console.error('Error in MoveProcessorOnTrack:', err);
                    reject(err);
                } else {
                    console.log('MoveProcessorOnTrack response:', response);
                    resolve(response);
                }
            });
        });
    }

    getProcessorId(processorName) {
        return new Promise((resolve, reject) => {
            const client = this.clients.AudioGraphController;
            if (!client) {
                reject(new Error('AudioGraphController client is not initialized'));
                return;
            }
            
            console.log('Calling GetProcessorId');
            const request = {
                value: processorName
            };
            
            client.GetProcessorId(request, (err, response) => {
                if (err) {
                    console.error('Error in GetProcessorId:', err);
                    reject(err);
                } else {
                    console.log('GetProcessorId response:', response);
                    resolve(response.id);
                }
            });
        });
    }

    setProcessorBypassState(processorId, bypassState) {
        return new Promise((resolve, reject) => {
            const client = this.clients.AudioGraphController;
            if (!client) {
                reject(new Error('AudioGraphController client is not initialized'));
                return;
            }
            
            console.log('Calling SetProcessorBypassState');
            const request = {
                processor: { id: processorId },
                value: bypassState
            };
            
            client.SetProcessorBypassState(request, (err, response) => {
                if (err) {
                    console.error('Error in SetProcessorBypassState:', err);
                    reject(err);
                } else {
                    console.log('SetProcessorBypassState response:', response);
                    resolve(response);
                }
            });
        });
    }
    // 可以根据需要添加其他方法
}

export default new GrpcClient();